﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.Common;
using System.Reflection;

namespace WorkNodeFramework
{
    public class Select:SelectQuery<Select>
    {
        public Select()
            :base()
        {
        }

        public Select(DataProvider provider)
            : this()
        {
            base.Provider = provider;
        }

        public Select(params IQueryExpression[] expressions)
            :base(expressions)
        {
        }

        public Select(params string[] expressions)
            : base(expressions)
        {
        }

        public Select(DataProvider provider, params IQueryExpression[] expressions)
            : this(expressions)
        {
            base.Provider = provider;
        }

        public Select(DataProvider provider, params string[] expressions)
            : this(expressions)
        {
            base.Provider = provider;
        }

        public SelectInto Into(ITable table)
        {
            return new SelectInto(this, table);
        }

        public SelectInto Into<TEntity>()
            where TEntity : IEntity, new()
        {
            return Into((new TEntity()).Table);
        }

        public string BuildSelectQueryStatement()
        {
            ParameterIndexCollection pars = new ParameterIndexCollection();
            return BuildSelectQueryStatement(pars);
        }

        bool getRecordCount = false;
        internal bool GetRecordCount {
            get {
                return getRecordCount;
            }
        }

        string BuildSelectQueryStatement(ParameterIndexCollection parameters)
        {
            return Provider.BuildSelectQueryStatement(this, parameters);
        }

        public TEntity ExecuteEntity<TEntity>()
            where TEntity:class
        {
            TEntity entity = null; 
            DataTable table = ExecuteDataTable();

            if (table.Rows.Count != 0)
            {
                entity = Activator.CreateInstance<TEntity>();
                DataRow dr = table.Rows[0];
                Type type = typeof(TEntity);
                IFieldSet fieldSet = entity as IFieldSet;

                foreach (DataColumn col in table.Columns)
                {
                    object val = dr[col];
                    PropertyInfo p = type.GetProperty(col.ColumnName);
                    if (p != null && p.CanWrite)
                    {
                        if (val is DBNull)
                            val = null;
                        p.SetValue(entity, val, null);
                    }
                    else if (fieldSet != null)
                        fieldSet[col.ColumnName] = val;
                }
            }
            if (entity is IFieldSet)
                (entity as IFieldSet).DefaultStatus = FieldSetStatus.Ready;
            return entity;
        }

        public List<TEntity> ExecuteTypedList<TEntity>() {
            List<TEntity> items = new List<TEntity>();
            Type type = typeof(TEntity);
            DataTable table = ExecuteDataTable();
            foreach (DataRow dr in table.Rows) {
                TEntity entity = Activator.CreateInstance<TEntity>();
                IFieldSet fieldSet = entity as IFieldSet;
                foreach (DataColumn col in table.Columns)
                {
                    object val = dr[col];
                    PropertyInfo p = type.GetProperty(col.ColumnName);
                    if (p != null && p.CanWrite)
                    {
                        if (val is DBNull)
                            val = null;
                        p.SetValue(entity, val, null);
                    }
                    else if (fieldSet != null)
                        fieldSet[col.ColumnName] = val;
                }
                if (entity is IFieldSet)
                    (entity as IFieldSet).DefaultStatus = FieldSetStatus.Ready;
                items.Add(entity);
            }
            return items;
        }

        public int Count()
        {
            getRecordCount = true;
            return (int)ExecuteScalar();
        }

        public IDataReader ExecuteReader()
        {
            ParameterIndexCollection pars = new ParameterIndexCollection();
            string cmdText = Provider.BuildSelectQueryStatement(this,pars);
            return Provider.ExecuteReader(cmdText, pars.ToDbParameters(Provider));
        }

        public object ExecuteScalar()
        {
            ParameterIndexCollection pars = new ParameterIndexCollection();
            string cmdText = Provider.BuildSelectQueryStatement(this, pars);
            return Provider.ExecuteScalar(cmdText, pars.ToDbParameters(Provider));
        }

        public DataTable ExecuteDataTable()
        {
            ParameterIndexCollection pars = new ParameterIndexCollection();
            string cmdText = Provider.BuildSelectQueryStatement(this, pars);
            DataTable data = Provider.ExecuteDataTable(cmdText, pars.ToDbParameters(Provider));
            data.TableName = TableList[0].Name;
            return data;
        }

        public DataSet ExecuteDataSet()
        {
            ParameterIndexCollection pars = new ParameterIndexCollection();
            string cmdText = Provider.BuildSelectQueryStatement(this, pars);
            return Provider.ExecuteDataSet(cmdText, pars.ToDbParameters(Provider));
        }
    }

    public class SelectInto : SelectQuery<SelectInto>, IExecuteCommand
    {
        ITable table;

        internal SelectInto(Select select,ITable table)
        {
            this.table = table;
            base.TableList.AddRange(select.TableList.ToArray());
            base.Provider = select.Provider;
            base.QueryList.AddRange(select.QueryList.ToArray());    
        }

        internal ITable IntoTable {
            get {
                return table;
            }
        }

        public string BuildSelectQueryStatement()
        {
            ParameterIndexCollection pars = new ParameterIndexCollection();
            return base.Provider.BuildSelectQueryStatement(this,pars);
        }

        public int Execute()
        {
            ParameterIndexCollection pars = new ParameterIndexCollection();
            string cmdText = base.Provider.BuildSelectQueryStatement(this, pars);
            return base.Provider.ExecuteNonQuery(cmdText, pars.ToDbParameters(base.Provider));
        }

        #region IExecuteCommand Members

        public ExecuteCommand BuildExecuteCommand()
        {
            ParameterIndexCollection pars = new ParameterIndexCollection();
            string cmdText = base.Provider.BuildSelectQueryStatement(this, pars);
            return new ExecuteCommand(cmdText, pars.ToDbParameters(base.Provider));
        }

        #endregion
    }
}
